// 项目设计数据库操作（基于mysql和oracle）
package daos

import (
	"bsm/config"
	"bsm/logger"
	"bsm/models"
	"errors"
	"fmt"
	"time"

	_ "github.com/sijms/go-ora/v2"
)

func (dao *designDaoImpl) Begin() error {
	if dao.txDesign != nil {
		return errors.New("事务已存在")
	}
	tx, err := dao.dbBsm.Beginx()
	if err != nil {
		return err
	}
	dao.txDesign = tx
	return nil
}

func (dao *designDaoImpl) Commit() error {
	if dao.txDesign == nil {
		return errors.New("没有活动的事务")
	}
	err := dao.txDesign.Commit()
	dao.txDesign = nil // 关键修复：提交后清理事务
	if err != nil {
		return fmt.Errorf("failed to commit pms transaction: %w", err)
	}
	return nil
}

func (dao *designDaoImpl) Rollback() error {
	if dao.txDesign == nil {
		return errors.New("没有活动的事务")
	}
	err := dao.txDesign.Rollback()
	dao.txDesign = nil // 关键修复：回滚后清理事务
	if err != nil {
		return fmt.Errorf("rollback errors: pms=%v", err)
	}
	return nil
}

// 事务模板方法（推荐）
func (d *designDaoImpl) InTransaction(fn func() error) error {
	if d.txDesign != nil {
		d.Rollback() // 清理已有事务
		return errors.New("事务已存在")
	}

	if err := d.Begin(); err != nil {
		return err
	}

	defer func() {
		if r := recover(); r != nil {
			d.Rollback()
			logger.Errorf("事务因panic回滚: %v", r)
		}
	}()

	if err := fn(); err != nil {
		d.Rollback()
		return err
	}

	return d.Commit()
}

// SetProjectBase 设置项目基本信息(设计编码存在则更新项目，不存在则新建项目)
func (dao *designDaoImpl) SetProjectBase(project *models.ProjectBase) error {
	project.ProjectState = models.ProjectState_Budget
	project.PreparationDate.Time = time.Now()

	// 确保 design_code 有唯一索引或主键约束
	query := `
        INSERT INTO project_base 
            (name, design_code, approval_code, initiator, business_type,project_type,construction_type, 
             project_overview, technical_analysis, point_old, point_new,point_cost,recycle_cycle,profit_statement, 
             org_code, designer, reviewer, project_state, preparation_date) 
        VALUES 
            (:name, :design_code, :approval_code, :initiator, :business_type,:project_type,:construction_type, 
             :project_overview, :technical_analysis, :point_old, :point_new, :point_cost,:recycle_cycle,:profit_statement,
             :org_code, :designer, :reviewer, :project_state, :preparation_date)	
        ON DUPLICATE KEY UPDATE
            name = VALUES(name),
            approval_code = VALUES(approval_code),
            initiator = VALUES(initiator),
            project_type = VALUES(project_type),
			construction_type = VALUES(construction_type),
			business_type = VALUES(business_type),
            project_overview = VALUES(project_overview),
            technical_analysis = VALUES(technical_analysis),
            point_old = VALUES(point_old),
            point_new = VALUES(point_new),
			point_cost = VALUES(point_cost),
			recycle_cycle = VALUES(recycle_cycle),
			profit_statement = VALUES(profit_statement),
            org_code = VALUES(org_code),
            designer = VALUES(designer),
            reviewer = VALUES(reviewer),
            project_state = VALUES(project_state),
            preparation_date = VALUES(preparation_date)
    `

	_, err := dao.txDesign.NamedExec(query, project)
	return err
}

// SetProjectConfig 设置项目设计配置
func (dao *designDaoImpl) SetProjectConfig(config models.ProjectDesignConfig) error {
	const query = `
	INSERT INTO project_config (design_code,discount_labor,discount_machine,discount_instrument,discount_measure,discount_management,discount_profit,safe_rate,vat_rate) values(:design_code,:discount_labor,:discount_machine,:discount_instrument,:discount_measure,:discount_management,:discount_profit,:safe_rate,:vat_rate)`
	param := map[string]interface{}{
		"design_code":         config.DesignCode,
		"discount_labor":      config.DiscountRates.DiscountLabor,
		"discount_machine":    config.DiscountRates.DiscountMachine,
		"discount_instrument": config.DiscountRates.DiscountInstrument,
		"discount_measure":    config.DiscountRates.DiscountMeasure,
		"discount_management": config.DiscountRates.DiscountManagement,
		"discount_profit":     config.DiscountRates.DiscountProfit,
		"safe_rate":           config.SafeRate,
		"vat_rate":            config.VatRate,
	}

	_, err := dao.dbBsm.NamedExec(query, param)
	return err
}

func (dao *designDaoImpl) ClearProjectLabors(pcode string, dtype int) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	sql := "delete c from project_labor_conversion c inner join project_labor l on c.lid=l.lid where l.design_code=:design_code and l.design_type=:design_type"

	param := map[string]interface{}{
		"design_code": pcode,
		"design_type": dtype,
	}
	_, err := dao.txDesign.NamedExec(sql, param)
	if err != nil {
		return err
	}

	sql = "delete m from project_machine m inner join project_labor l on m.lid=l.lid where l.design_code=:design_code and l.design_type=:design_type"
	_, err = dao.txDesign.NamedExec(sql, param)
	if err != nil {
		return err
	}

	sql = "delete i from project_instrument i inner join project_labor l on i.lid=l.lid where l.design_code=:design_code and l.design_type=:design_type"
	_, err = dao.txDesign.NamedExec(sql, param)
	if err != nil {
		return err
	}

	sql = "delete  from project_labor where design_code=:design_code and design_type=:design_type"
	_, err = dao.txDesign.NamedExec(sql, param)
	if err != nil {
		return err
	}

	return nil

}

func (dao *designDaoImpl) SetProjectLabors(designcode string, designtype int, itmes []models.LaborItem) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	pcode := designcode
	dtype := designtype
	query := "delete from project_labor where design_code=:design_code and design_type=:design_type"
	param := map[string]interface{}{
		"design_code": pcode,
		"design_type": dtype,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 如果数据为空，则清空后返回
	if len(itmes) == 0 {
		return nil
	}

	query = "insert into project_labor (lid,design_code,design_type,code,quantity,remark) values (:lid,:design_code,:design_type,:code,:quantity,:remark)"

	_, err = dao.txDesign.NamedExec(query, itmes)
	if err != nil {
		logger.Errorf("SetProjectLabors failed - pcode: %s, error: %v", pcode, err)
	}

	for _, item := range itmes {
		err = dao.SetProjectConversions(item.LID, item.Conversions)
		if err != nil {
			logger.Errorf("SetProjectConversions failed - lid: %s, error: %v", item.LID, err)
		}
		err = dao.SetProjectMachines(item.LID, item.Machines)
		if err != nil {
			logger.Errorf("SetProjectMachines failed - lid: %s, error: %v", item.LID, err)
		}
		err = dao.SetProjectInstruments(item.LID, item.Instruments)
		if err != nil {
			logger.Errorf("SetProjectInstruments failed - lid: %s, error: %v", item.LID, err)
		}
	}
	return err
}

func (dao *designDaoImpl) SetProjectConversions(labor_id string, items []models.NormConversionItem) error {

	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}
	query := "delete from project_labor_conversion where lid=:lid"
	param := map[string]interface{}{
		"lid": labor_id,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 空数据直接返回
	if len(items) == 0 {
		return nil
	}

	query = "insert into project_labor_conversion (lid,nci_id) values (:lid,:nci_id)"
	var conversions []map[string]interface{}
	for _, item := range items {
		conversions = append(conversions, map[string]interface{}{
			"lid":    labor_id,
			"nci_id": item.NciID,
		})
	}
	_, err = dao.txDesign.NamedExec(query, conversions)
	if err != nil {
		return err
	}
	return nil

}

func (dao *designDaoImpl) SetLaborMachines(items []models.LaborItem) error {
	// 空数据直接返回
	if len(items) == 0 {
		return nil
	}
	var err error
	query := "select mach_id from norm_machine where norm_code=?"
	for i := range items {
		var mach_ids []int
		err := dao.txDesign.Select(&mach_ids, query, items[i].Code)
		if err != nil {
			logger.Errorf("SetLaborMachines failed - norm_code: %s, error: %v", items[i].Code, err)
		}
		items[i].Machines = make([]models.MachineItem, 0, len(mach_ids))
		for _, machID := range mach_ids {
			items[i].Machines = append(items[i].Machines, models.MachineItem{
				LID:      items[i].LID,
				MachID:   machID,
				Quantity: items[i].Quantity,
				Remark:   items[i].Remark,
			})
		}
	}
	return err
}

func (dao *designDaoImpl) SetLaborInstruments(items []models.LaborItem) error {
	// 空数据直接返回
	if len(items) == 0 {
		return nil
	}
	var err error
	query := "select inst_id from norm_instrument where norm_code=?"
	for i := range items {
		var inst_ids []int
		err := dao.txDesign.Select(&inst_ids, query, items[i].Code)
		if err != nil {
			logger.Errorf("SetLaborInstruments failed - norm_code: %s, error: %v", items[i].Code, err)
		}
		items[i].Instruments = make([]models.InstrumentItem, 0, len(inst_ids))
		for _, inst_id := range inst_ids {
			items[i].Instruments = append(items[i].Instruments, models.InstrumentItem{
				LID:      items[i].LID,
				InstID:   inst_id,
				Quantity: items[i].Quantity,
				Remark:   items[i].Remark,
			})
		}
	}
	return err
}

//

func (dao *designDaoImpl) SetProjectMachines(labor_id string, items []models.MachineItem) error {

	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	query := "delete from project_machine where lid=:lid"
	param := map[string]interface{}{
		"lid": labor_id,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 空数据直接返回
	if len(items) == 0 {

		return nil
	}

	query = "insert into project_machine (lid,mach_id,quantity,remark) values (:lid,:mach_id,:quantity,:remark)"

	var machines []map[string]interface{}
	for _, item := range items {
		machines = append(machines, map[string]interface{}{
			"lid":      labor_id,
			"mach_id":  item.MachID,
			"quantity": item.Quantity,
			"remark":   item.Remark,
		})
	}
	_, err = dao.txDesign.NamedExec(query, machines)
	if err != nil {
		logger.Errorf("SetProjectMachines failed - lid: %s, error: %v", labor_id, err)
	}

	return nil
}

func (dao *designDaoImpl) SetProjectInstruments(labor_id string, items []models.InstrumentItem) error {

	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	query := "delete from project_instrument where lid=:lid"
	param := map[string]interface{}{
		"lid": labor_id,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 空数据直接返回
	if len(items) == 0 {

		return nil
	}

	query = "insert into project_instrument (lid,inst_id,quantity,remark) values (:lid,:inst_id,:quantity,:remark)"
	var instruments []map[string]interface{}
	for _, item := range items {
		instruments = append(instruments, map[string]interface{}{
			"lid":      labor_id,
			"inst_id":  item.InstID,
			"quantity": item.Quantity,
			"remark":   item.Remark,
		})
	}
	_, err = dao.txDesign.NamedExec(query, instruments)
	if err != nil {
		logger.Errorf("SetProjectInstruments failed - lid: %s, error: %v", labor_id, err)

	}
	return nil
}

func (dao *designDaoImpl) SetProjectMaterials(pcode string, dtype int, items []models.MaterialItem) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	query := "delete from project_material where design_code=:design_code and design_type=:design_type"
	param := map[string]interface{}{
		"design_code": pcode,
		"design_type": dtype,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 空数据直接返回
	if len(items) == 0 {
		return nil
	}

	query = "insert into project_material (design_code,design_type,code,price,quantity,remark) values (:design_code,:design_type,:code,:price,:quantity,:remark)"

	var materials []map[string]interface{}
	for _, item := range items {
		materials = append(materials, map[string]interface{}{
			"design_code": pcode,
			"design_type": dtype,
			"code":        item.Code,
			"price":       item.Price,
			"quantity":    item.Quantity,
			"remark":      item.Remark,
		})
	}
	_, err = dao.txDesign.NamedExec(query, materials)
	if err != nil {
		//logger.Errorf("SetProjectMaterials failed - pcode: %s, error: %v", pcode, err)
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "designDaoImpl-SetProjectMaterials",
			"pcode":     pcode,
			"dtype":     dtype,
		}).Errorf("SetProjectMaterials failed - pcode: %s, error: %v", pcode, err)
		return err
	}
	return nil
}

func (dao *designDaoImpl) SetProjectOtherFee(designcode string, designtype int, items []models.OtherFeeItem) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	query := "delete from project_other where design_code=:design_code and design_type=:design_type"
	param := map[string]interface{}{
		"design_code": designcode,
		"design_type": designtype,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}

	// 空数据直接返回
	if len(items) == 0 {
		return nil
	}

	query = "insert into project_other (design_code,design_type,serial_number,name,basis,tax_excluded,tax,tax_included,remark) values (:design_code,:design_type,:serial_number,:name,:basis,:tax_excluded,:tax,:tax_included,:remark)"

	var otherfees []map[string]interface{}
	for i, item := range items {
		serial_number := i + 17
		tax_ex := item.TaxIncluded / (1 + config.Get().DesignConfig.VatRate)
		tax := item.TaxIncluded - tax_ex

		otherfees = append(otherfees, map[string]interface{}{
			"design_code":   designcode,
			"design_type":   designtype,
			"serial_number": serial_number,
			"name":          item.Name,
			"basis":         item.Basis,
			"tax_excluded":  tax_ex,
			"tax":           tax,
			"tax_included":  item.TaxIncluded,
			"remark":        item.Remark,
		})
	}
	_, err = dao.txDesign.NamedExec(query, otherfees)
	if err != nil {
		return err
	}
	return nil
}

func (dao *designDaoImpl) ClearProjectMaterials(pcode string, dtype int) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}
	query := "delete from project_material where design_code=:design_code and design_type=:design_type"
	param := map[string]interface{}{
		"design_code": pcode,
		"design_type": dtype,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}
	return nil
}

func (dao *designDaoImpl) ClearProjectOtherFee(pcode string, dtype int) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}
	query := "delete from project_other where design_code=:design_code and design_type=:design_type"
	param := map[string]interface{}{
		"design_code": pcode,
		"design_type": dtype,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}
	return nil
}

// 更新项目各阶段最后更改时间，转入下阶段后不得更改
func (dao *designDaoImpl) UpdateDesignTime(designcode string, state int) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}
	var query string

	switch state {
	case models.ProjectState_Budget:
		query = "update project_base set time_budget=now() where design_code=:design_code and project_state=:project_state"
	case models.ProjectState_Accept:
		query = "update project_base set time_settle=now() where design_code=:design_code and project_state=:project_state"
	case models.ProjectState_Audit:
		query = "update project_base set time_audit=now() where design_code=:design_code and project_state=:project_state"
	default:
		return errors.New("项目状态错误")
	}
	param := map[string]interface{}{
		"design_code":   designcode,
		"project_state": state,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}
	return nil
}

func (dao *designDaoImpl) SetProjectState(pcode string, state int) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}

	query := "update project_base set project_state=:project_state where design_code=:design_code"
	param := map[string]interface{}{
		"design_code":   pcode,
		"project_state": state,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		return err
	}
	return nil
}

func (dao *designDaoImpl) DeleteProjectBase(designcode string) error {
	if dao.txDesign == nil {
		return errors.New("必须在事务中调用")
	}
	query := "delete from project_config where design_code=:design_code"
	param := map[string]interface{}{
		"design_code": designcode,
	}
	_, err := dao.txDesign.NamedExec(query, param)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "DeleteProjectBase",
			"error":     err.Error(),
		}).Error("删除项目配置失败")
		return err
	}

	query = "delete from project_base where design_code=:design_code"
	_, err = dao.txDesign.NamedExec(query, param)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "DeleteProjectBase",
			"error":     err.Error(),
		}).Error("删除项目基本信息失败")
		return err
	}
	return nil
}
